package com.ruoyi.activiti.controller.definition;

import com.ruoyi.activiti.domain.ProcessDefinitionEntity;
import com.ruoyi.activiti.service.IProcessDefinitionService;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

import com.ruoyi.common.core.utils.FileUploadUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.utils.poi.ExcelUtil;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.common.core.web.page.TableDataInfo;
import io.swagger.annotations.ApiOperation;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.image.ProcessDiagramGenerator;
import org.apache.commons.io.IOUtils;
import org.apache.ibatis.annotations.Param;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.List;


@Controller
@RequestMapping("/definition")
public class ProcessDefinitionController extends BaseController {

    private static final Logger log = LoggerFactory.getLogger(ProcessDefinitionController.class);

    @Autowired
    private IProcessDefinitionService processDefinitionService;
    @Resource
    private RepositoryService repositoryService;

    @Resource
    ProcessEngineConfiguration configuration;

    @Value("${ruoyi.profile}")
    private String uploadPath;


    /**
     * 流程定义列表
     */
    @GetMapping("/list")
    @ResponseBody
    public TableDataInfo list(ProcessDefinitionEntity processDefinition) {
        List<ProcessDefinitionEntity> list = processDefinitionService.listProcessDefinition(processDefinition);
        return getDataTable(list);
    }

    /**
     * 上传部署流程定义
     */
    @PostMapping("/upload")
    @ResponseBody
    public AjaxResult upload(/*@RequestParam("processDefinition") */MultipartFile file) {
        try {
            if (!file.isEmpty() && file.getOriginalFilename() != null) {
                String extensionName = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf('.') + 1);
                if (!"bpmn".equalsIgnoreCase(extensionName)
                        && !"zip".equalsIgnoreCase(extensionName)
                        && !"bar".equalsIgnoreCase(extensionName)) {
                    return error("流程定义文件仅支持 bpmn, zip 和 bar 格式！");
                }
                String fileName = FileUploadUtils.upload(uploadPath , file);
                if (StringUtils.isNotBlank(fileName)) {
                    processDefinitionService.deployProcessDefinition(uploadPath+fileName);
                    return success();
                }
            }
            return error("不允许上传空文件！");
        }
        catch (Exception e) {
            log.error("上传流程定义文件失败！", e);
            return error(e.getMessage());
        }
    }

    /**
     * 删除流程定义
     * @param deploymentId
     * @return
     */
    @DeleteMapping("/remove/{deploymentId}")
    @ResponseBody
    public AjaxResult remove(@PathVariable String deploymentId) {
        try {
            return toAjax(processDefinitionService.deleteProcessDeploymentByIds(deploymentId));
        }
        catch (Exception e) {
            return error(e.getMessage());
        }
    }

    /**
     * 导出流程定义
     * @param response
     * @throws IOException
     */
    @PostMapping("/export")
    @ResponseBody
    public void export(HttpServletResponse response) throws IOException {
        List<ProcessDefinitionEntity> list = processDefinitionService.listProcessDefinition(new ProcessDefinitionEntity());
        ExcelUtil<ProcessDefinitionEntity> util = new ExcelUtil<>(ProcessDefinitionEntity.class);
        util.exportExcel(response,list, "流程定义数据");
    }


    /**
     * 流程定义挂起/激活
     * @param id
     * @param suspendState
     * @return
     */
    @PostMapping( "/suspendOrActiveDefinition")
    @ResponseBody
    public AjaxResult suspendOrActiveDefinition(String id, String suspendState) {
        processDefinitionService.suspendOrActiveDefinition(id, suspendState);
        return success();
    }

    /**
     * 读取流程资源
     */
    @RequestMapping(value = "/readResource")
    public void readResource(@RequestParam("pdid") String processDefinitionId, @RequestParam("resourceName") String resourceName, HttpServletResponse response)
            throws Exception {
        ProcessDefinitionQuery pdq = repositoryService.createProcessDefinitionQuery();
        org.activiti.engine.repository.ProcessDefinition pd = pdq.processDefinitionId(processDefinitionId).singleResult();

        // 通过接口读取
        InputStream resourceAsStream = repositoryService.getResourceAsStream(pd.getDeploymentId(), resourceName);

        // 输出资源内容到相应对象
        byte[] b = new byte[1024];
        int len = -1;
        while ((len = resourceAsStream.read(b, 0, 1024)) != -1) {
            response.getOutputStream().write(b, 0, len);
        }
    }

    /**
     * 转换流程定义为模型
     */
    @PostMapping(value = "/convert2Model")
    @ResponseBody
    public AjaxResult convertToModel(@Param("processDefinitionId") String processDefinitionId) throws XMLStreamException {
        org.activiti.engine.repository.ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(processDefinitionId).singleResult();
        InputStream bpmnStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
                processDefinition.getResourceName());
        XMLInputFactory xif = XMLInputFactory.newInstance();
        InputStreamReader in = new InputStreamReader(bpmnStream, StandardCharsets.UTF_8);
        XMLStreamReader xtr = xif.createXMLStreamReader(in);
        BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);

        BpmnJsonConverter converter = new BpmnJsonConverter();
        ObjectNode modelNode = converter.convertToJson(bpmnModel);
        Model modelData = repositoryService.newModel();
        modelData.setKey(processDefinition.getKey());
        modelData.setName(processDefinition.getResourceName());
        modelData.setCategory(processDefinition.getDeploymentId());

        ObjectNode modelObjectNode = new ObjectMapper().createObjectNode();
        modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, processDefinition.getName());
        modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
        modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, processDefinition.getDescription());
        modelData.setMetaInfo(modelObjectNode.toString());

        repositoryService.saveModel(modelData);
        repositoryService.addModelEditorSource(modelData.getId(), modelNode.toString().getBytes(StandardCharsets.UTF_8));

        return success();
    }

    @ApiOperation("查看工作流图片")
    @GetMapping(value = "/showresource")
    public void showresource(@RequestParam("pdid") String pdid, HttpServletResponse response) throws Exception {
        BpmnModel bpmnModel = repositoryService.getBpmnModel(pdid);
        ProcessDiagramGenerator diagramGenerator = configuration.getProcessDiagramGenerator();
        InputStream is = diagramGenerator.generateDiagram(bpmnModel, "png",  "宋体", "宋体", "宋体", configuration.getClassLoader(), 1.0);
        ServletOutputStream output = response.getOutputStream();
        IOUtils.copy(is, output);
    }

    @ApiOperation("查看工作流定义")
    @GetMapping(value = "/showProcessDefinition1")
    public void showProcessDefinition1(@RequestParam("deploymentId") String deploymentId, @RequestParam(value="resourceName") String resourceName,
                                      HttpServletResponse response) throws Exception {
        InputStream is = repositoryService.getResourceAsStream(deploymentId, resourceName);
        ServletOutputStream output = response.getOutputStream();
        IOUtils.copy(is, output);
    }

    @ApiOperation("查看工作流定义")
    @GetMapping(value = "/showProcessDefinition")
    @ResponseBody
    public AjaxResult showProcessDefinition(@RequestParam("deploymentId") String deploymentId, @RequestParam(value="resourceName") String resourceName,
                                      HttpServletResponse response) throws Exception {
        InputStream is = repositoryService.getResourceAsStream(deploymentId, resourceName);
        try {

            // 使用 InputStreamReader 将 InputStream 转化为文本
            InputStreamReader isr = new InputStreamReader(is, "UTF-8"); // 指定字符集编码
            BufferedReader reader = new BufferedReader(isr);

            StringBuilder textData = new StringBuilder();
            String line;

            while ((line = reader.readLine()) != null) {
                textData.append(line).append("\n");
            }

            // 最后，textData 中包含了从 InputStream 中读取的文本数据
            String textContent = textData.toString();

            return success("请求成功", textContent);

        } catch (IOException e) {
            e.printStackTrace();
        }
        return success();
    }

}
